﻿using System;
using System.Collections.Generic;
using System.IO;
using System.IO.IsolatedStorage;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Xml.Serialization;

/**
 * 创建时间：2014/11/18 15:10:53
 * 作者：yong.blackcore
 * Email：itblackhole@outlook.com
 * QQ：50404503
 */
namespace CUWo.Common.StorageHelper
{
    public class IsolatedStorageHelper : IStorageHelper
    {
        private static readonly Enough.Async.AsyncLock asyncLock = new Enough.Async.AsyncLock();
        private static readonly object lockObj = new object();

        private readonly IsolatedStorageFile storageFile;

        #region 提供单例的支持

        public static IStorageHelper Instance { get; private set; }

        public static object LockObject;

        static IsolatedStorageHelper()
        {
            Instance = new IsolatedStorageHelper();
            LockObject = new object();
        }

        private IsolatedStorageHelper()
        {
            storageFile = IsolatedStorageFile.GetUserStoreForApplication();
        }

        #endregion

        public Stream ReadFile(string filePath)
        {
            lock (lockObj)
            {
                if (!storageFile.FileExists(filePath))
                {
                    throw new FileNotFoundException(string.Format("没有找到文件:{0}", filePath));
                }

                using (var fs = new IsolatedStorageFileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, storageFile))
                {
                    var stream = new MemoryStream();
                    fs.CopyTo(stream);

                    stream.Seek(0, SeekOrigin.Begin);
                    return stream;
                }
            }
        }

        public string ReadText(string filePath)
        {
            lock (lockObj)
            {
                using (var stream = new IsolatedStorageFileStream(filePath, FileMode.Open, storageFile))
                {
                    using (var r = new StreamReader(stream))
                    {
                        return r.ReadToEnd();
                    }
                }
            }
        }

        public void WriteFile(Stream stream, string filePath, bool replace = false)
        {
            WriteFile(ToBytes(stream), filePath, replace);
        }

        public void WriteFile(byte[] data, string filePath, bool replace = false)
        {
            lock (lockObj)
            {
                var directory = Path.GetDirectoryName(filePath);
                if (directory != null)
                {
                    directory = directory.Replace("\\", "/");
                    if (!storageFile.DirectoryExists(directory))
                    {
                        //如果目录不存在，则创建
                        storageFile.CreateDirectory(directory);
                    }
                }

                if (storageFile.FileExists(filePath))
                {
                    if (replace)
                    {
                        storageFile.DeleteFile(filePath);
                    }
                    else
                    {
                        return;
                    }
                }

                using (var fs = new IsolatedStorageFileStream(filePath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None, storageFile))
                {
                    fs.Write(data, 0, data.Length);
                }
            }
        }

        public void WriteText(string text, string filePath, bool replace = false)
        {
            WriteFile(Encoding.UTF8.GetBytes(text), filePath, replace);
        }

        public bool FileExists(string filePath)
        {
            lock (lockObj)
            {
                return storageFile.FileExists(filePath);
            }
        }

        public bool DirectoryExists(string directory)
        {
            lock (lockObj)
            {
                return storageFile.DirectoryExists(directory);
            }
        }

        public bool DeleteFile(string filePath)
        {
            lock (lockObj)
            {
                if (!storageFile.FileExists(filePath)) return false;
                storageFile.DeleteFile(filePath);
                return true;
            }
        }

        public bool DeleteDirectory(string directory, bool isDeleteAll)
        {
            lock (lockObj)
            {
                if (storageFile.GetFileNames(directory).Length + storageFile.GetDirectoryNames(directory).Length > 0)
                {
                    if (isDeleteAll)
                    {
                        DeleteDirectory(directory);
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }
                else
                {
                    storageFile.DeleteDirectory(directory);
                    return true;
                }
            }
        }

        public bool CreateDirectory(string directory)
        {
            lock (lockObj)
            {
                if (storageFile.DirectoryExists(directory)) return false;
                storageFile.CreateDirectory(directory);
                return true;
            }
        }

        public long GetFileLength(string filePath)
        {
            lock (lockObj)
            {
                if (storageFile.FileExists(filePath))
                {
                    return storageFile.OpenFile(filePath, FileMode.Open, FileAccess.Read).Length;
                }
                throw new FileNotFoundException(string.Format("没有找到文件:{0}", filePath));
            }
        }


        public string[] GetFiles(string directory)
        {
            lock (lockObj)
            {
                var files = storageFile.GetFileNames(directory);
                return files.Select(f => directory + f).ToArray();
            }
        }

        public string[] GetDirectories(string directory)
        {
            lock (lockObj)
            {
                var folders = storageFile.GetDirectoryNames(directory);
                return folders.Select(f => directory + f).ToArray();
            }
        }

        public void Serialize<T>(string filePath, T obj)
        {
            lock (lockObj)
            {
                var directory = Path.GetDirectoryName(filePath);
                if (directory != null)
                {
                    directory = directory.Replace("\\", "/");
                    if (!storageFile.DirectoryExists(directory))
                    {
                        //如果目录不存在，则创建
                        storageFile.CreateDirectory(directory);
                    }
                }

                if (storageFile.FileExists(filePath))
                {
                    storageFile.DeleteFile(filePath);
                }

                using (
                    var fs = new IsolatedStorageFileStream(filePath, FileMode.OpenOrCreate, FileAccess.Write,
                        FileShare.None, storageFile))
                {
                    var serializer = new XmlSerializer(obj.GetType());
                    serializer.Serialize(fs, obj);
                }
            }
        }

        public T DeSerialize<T>(string filePath)
        {
            lock (lockObj)
            {
                if (!storageFile.FileExists(filePath))
                {
                    throw new FileNotFoundException(string.Format("没有找到文件:{0}", filePath));
                }
                var serializer = new XmlSerializer(typeof(T));
                using (var fs = storageFile.OpenFile(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
                {
                    return (T)serializer.Deserialize(fs);
                }
            }
        }

        public void CopyPackageFileToLocal(string source, string target = null, bool replace = false)
        {
            using (var stream = Application.GetResourceStream(new Uri(source, UriKind.Relative)).Stream)
            {
                WriteFile(stream, target ?? source, replace);
            }
        }

        public void CopyPackageFolderToLocal(string source, string target = null, bool replace = false)
        {
            throw new NotImplementedException("IsolatedStorageFile不支持拷贝安装文件夹");
        }

        public Stream GetResourceStream(string filePath)
        {
            return Application.GetResourceStream(new Uri(filePath, UriKind.Relative)).Stream;
        }

        public async Task<Stream> ReadFileAsync(string filePath)
        {
            using (await asyncLock.LockAsync())
            {
                return await Task.Factory.StartNew(() => ReadFile(filePath));
            }
        }

        public async Task<string> ReadTextAsync(string filePath)
        {
            using (await asyncLock.LockAsync())
            {
                return await Task.Factory.StartNew(() => ReadText(filePath));
            }
        }

        public async Task WriteFileAsync(Stream stream, string filePath, bool replace = false)
        {
            await WriteFileAsync(ToBytes(stream), filePath, replace);
        }

        public async Task WriteFileAsync(byte[] data, string filePath, bool replace = false)
        {
            using (await asyncLock.LockAsync())
            {
                await Task.Factory.StartNew(() => WriteFile(data, filePath, replace));
            }
        }

        public async Task WriteTextAsync(string text, string filePath, bool replace = false)
        {
            await WriteFileAsync(Encoding.UTF8.GetBytes(text), filePath, replace);
        }

        public async Task<bool> FileExistsAsync(string filePath)
        {
            using (await asyncLock.LockAsync())
            {
                return await Task.Factory.StartNew(() => FileExists(filePath));
            }
        }

        public async Task<bool> DirectoryExistsAsync(string directory)
        {
            using (await asyncLock.LockAsync())
            {
                return await Task.Factory.StartNew(() => DirectoryExists(directory));
            }
        }

        public async Task<bool> DeleteFileAsync(string filePath)
        {
            using (await asyncLock.LockAsync())
            {
                return await await Task.Factory.StartNew(() => DeleteFileAsync(filePath));
            }
        }

        public async Task<bool> DeleteDirectoryAsync(string directory, bool isDeleteAll = false)
        {
            using (await asyncLock.LockAsync())
            {
                return await Task.Factory.StartNew(() => DeleteDirectory(directory, isDeleteAll));
            }

        }

        public async Task<bool> CreateDirectoryAsync(string directory)
        {
            using (await asyncLock.LockAsync())
            {
                return await Task.Factory.StartNew(() => CreateDirectory(directory));
            }
        }

        public async Task<ulong> GetFileLengthAsync(string filePath)
        {
            using (await asyncLock.LockAsync())
            {
                return await Task.Factory.StartNew(() => (ulong)GetFileLength(filePath));
            }
        }

        public async Task<string[]> GetFilesAsync(string directory)
        {
            using (await asyncLock.LockAsync())
            {
                return await Task.Factory.StartNew(() => GetFiles(directory));
            }
        }

        public async Task<string[]> GetDirectoriesAsync(string directory)
        {
            using (await asyncLock.LockAsync())
            {
                return await Task.Factory.StartNew(() => GetDirectories(directory));
            }
        }

        public async Task SerializeAsync<T>(string filePath, T obj)
        {
            using (await asyncLock.LockAsync())
            {
                await Task.Factory.StartNew(() => Serialize(filePath, obj));
            }
        }

        public async Task<T> DeSerializeAsync<T>(string filePath)
        {
            using (await asyncLock.LockAsync())
            {
                return await Task.Factory.StartNew(() => DeSerialize<T>(filePath));
            }
        }

        public async Task CopyPackageFileToLocalAsync(string source, string target = null, bool replace = false)
        {
            using (await asyncLock.LockAsync())
            {
                await Task.Factory.StartNew(() => CopyPackageFileToLocal(source, target, replace));
            }
        }

        public async Task CopyPackageFolderToLocalAsync(string source, string target = null, bool replace = false)
        {
            using (await asyncLock.LockAsync())
            {
                await Task.Factory.StartNew(() => CopyPackageFolderToLocal(source, target, replace));
            }
        }

        public async Task<Stream> GetResourceStreamAsync(string filePath)
        {
            using (await asyncLock.LockAsync())
            {
                return await Task.Factory.StartNew(() =>
                    Application.GetResourceStream(new Uri(filePath, UriKind.Relative)).Stream);
            }
        }

        #region 辅助函数

        //递归删除文件夹
        private void DeleteDirectory(string directory)
        {
            var directories = storageFile.GetDirectoryNames(directory);
            foreach (var d in directories)
            {
                DeleteDirectory(string.Format("{0}{1}/", directory, d));
            }
            var files = storageFile.GetFileNames(directory);
            foreach (var f in files)
            {
                storageFile.DeleteFile(f);
            }
        }

        private byte[] ToBytes(Stream stream)
        {
            if (stream.CanSeek)
            {
                stream.Seek(0, SeekOrigin.Begin);
            }
            var length = Convert.ToInt32(stream.Length);
            var data = new byte[length];
            stream.Read(data, 0, length);
            return data;
        }

        #endregion
    }
}
